/* Copyright (c) 2008-2010, developers of the Ascension Log Visualizer
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package com.googlecode.logVisualizer.chart.turnrundownGantt;
import java.awt.Adjustable;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Paint;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JScrollBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.java.dev.spellcast.utilities.UtilityConstants;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.IntervalCategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.data.gantt.SlidingGanttCategoryDataset;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.TextAnchor;
import com.googlecode.logVisualizer.chart.AbstractChart;
import com.googlecode.logVisualizer.logData.LogDataHolder;
import com.googlecode.logVisualizer.logData.logSummary.LevelData;
import com.googlecode.logVisualizer.logData.turn.turnAction.DayChange;
import com.googlecode.logVisualizer.logData.turn.turnAction.FamiliarChange;
import com.googlecode.logVisualizer.util.CategoryViewFileHandler;
import com.googlecode.logVisualizer.util.DataNumberPair;
import com.googlecode.logVisualizer.util.LookAheadIterator;
public abstract class GanttChartBuilder extends AbstractChart {
/**
*
*/
private static final long serialVersionUID = 1694279804243744892L;
private List<TurnAreaCategory> categories = new ArrayList<>();
private final Map<String, FamiliarColor> familiarColors = new LinkedHashMap<>();
private SlidingGanttCategoryDataset dataset;
private int lastTurnNumber = Integer.MIN_VALUE;
protected GanttChartBuilder(final String title, final LogDataHolder logData) {
super(title, logData, false);
try {
this.categories = CategoryViewFileHandler
.parseOutCategories(new File(
UtilityConstants.ROOT_DIRECTORY + File.separator
+ UtilityConstants.DATA_DIRECTORY
+ "standardView.cvw"));
} catch (final IOException e) {
e.printStackTrace();
}
// Create a list of all used familiars if there is none present
if (logData.getLogSummary().getFamiliarUsage().isEmpty()) {
final Set<String> usedFamiliars = new HashSet<>();
for (final FamiliarChange fc : logData.getFamiliarChanges()) {
usedFamiliars.add(fc.getFamiliarName());
}
for (final String s : usedFamiliars) {
this.familiarColors.put(s, new FamiliarColor(s, "none"));
}
} else {
for (final DataNumberPair<String> dn : logData.getLogSummary()
.getFamiliarUsage()) {
this.familiarColors.put(dn.getData(),
new FamiliarColor(dn.getData(), "none"));
}
}
this.addChart();
}
protected abstract SlidingGanttCategoryDataset createDataset();
private JFreeChart createChart(final SlidingGanttCategoryDataset dataset) {
this.dataset = dataset;
final JFreeChart chart = ChartFactory.createGanttChart(this.getTitle(),
null, null, dataset, false, true, false);
final CategoryPlot plot = (CategoryPlot) chart.getPlot();
final CategoryItemRenderer renderer = plot.getRenderer();
plot.getDomainAxis().setMaximumCategoryLabelWidthRatio(0.15f);
plot.setRangeAxis(new FixedZoomNumberAxis());
plot.getRangeAxis().setStandardTickUnits(
NumberAxis.createIntegerTickUnits());
plot.getRangeAxis().setAutoRange(false);
plot.setRangeGridlinePaint(Color.black);
AbstractChart.setBarShadowVisible(chart, false);
for (final AreaInterval ai : ((TurnRundownDataset) dataset
.getUnderlyingDataset()).getDataset()) {
if (this.lastTurnNumber < ai.getEndTurn()) {
this.lastTurnNumber = ai.getEndTurn();
}
}
this.addDayMarkers(plot);
this.addLevelMarkers(plot);
this.addFamiliarMarkers(plot);
plot.getRangeAxis().setUpperBound(this.lastTurnNumber + 10);
renderer.setSeriesPaint(0, Color.red);
renderer.setBaseToolTipGenerator(new IntervalCategoryToolTipGenerator(
"{1}, {3} - {4}", NumberFormat.getInstance()));
return chart;
}
private void addDayMarkers(final CategoryPlot plot) {
for (final DayChange dc : this.getLogData().getDayChanges()) {
final ValueMarker day = new ValueMarker(dc.getTurnNumber());
day.setLabel("Day " + dc.getDayNumber());
day.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
day.setLabelTextAnchor(TextAnchor.TOP_LEFT);
day.setStroke(new BasicStroke(2));
day.setPaint(Color.BLUE);
plot.addRangeMarker(day);
}
}
private void addLevelMarkers(final CategoryPlot plot) {
for (final LevelData ld : this.getLogData().getLevels()) {
final ValueMarker level = new ValueMarker(
ld.getLevelReachedOnTurn());
level.setLabel("Level " + ld.getLevelNumber());
level.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
level.setLabelTextAnchor(TextAnchor.BOTTOM_LEFT);
level.setStroke(new BasicStroke(2));
level.setPaint(new Color(0, 150, 0));
plot.addRangeMarker(level);
}
}
private void addFamiliarMarkers(final CategoryPlot plot) {
final LookAheadIterator<FamiliarChange> index = new LookAheadIterator<>(
this.getLogData().getFamiliarChanges().iterator());
while (index.hasNext()) {
final FamiliarChange currentItem = index.next();
final int intervalEnd = index.hasNext() ? index.peek()
.getTurnNumber() : this.lastTurnNumber;
final IntervalMarker familiarUsage = new IntervalMarker(
currentItem.getTurnNumber(), intervalEnd);
familiarUsage
.setPaint(this.getColor(currentItem.getFamiliarName()));
if (!familiarUsage.getPaint().equals(Color.white)) {
plot.addRangeMarker(familiarUsage, Layer.BACKGROUND);
}
}
}
private Paint getColor(final String familiarName) {
final FamiliarColor color = this.familiarColors.get(familiarName);
return color == null ? Color.white : color.getColorPaint();
}
public void addCategory(final TurnAreaCategory category) {
this.categories.add(category);
}
public void setCategories(final List<TurnAreaCategory> categories) {
this.categories = categories;
this.updateChart();
}
public List<TurnAreaCategory> getCategories() {
return this.categories;
}
public void setFamiliarColors(final List<FamiliarColor> familiarColors) {
this.familiarColors.clear();
for (final FamiliarColor color : familiarColors) {
this.familiarColors.put(color.getFamiliarName(), color);
}
}
public Collection<FamiliarColor> getFamiliarColors() {
return this.familiarColors.values();
}
public void updateChart() {
this.removeAll();
this.addChart();
this.updateUI();
}
@Override
protected void addChart() {
super.addChart();
final int scrollCaretExtend = 20;
int scrollableAreaIntervals = ((TurnRundownDataset) this.dataset
.getUnderlyingDataset()).getDataset().size()
- (this.dataset.getMaximumCategoryCount() - scrollCaretExtend);
scrollableAreaIntervals = scrollableAreaIntervals > 20 ? scrollableAreaIntervals
: 20;
final JScrollBar scrollBar = new JScrollBar(Adjustable.VERTICAL, 0,
scrollCaretExtend, 0, scrollableAreaIntervals);
scrollBar.getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
GanttChartBuilder.this.dataset.setFirstCategoryIndex(scrollBar
.getValue());
}
});
this.add(scrollBar, BorderLayout.EAST);
}
@Override
protected ChartPanel createChartPanel() {
return new ChartPanel(this.createChart(this.createDataset()));
}
/**
* A "hacked" NumberAxis class. The only difference is that it zooms out to
* 0 to maximum turncount + 10 instead of simply turning autoRange on, which
* gives less than desirable results when scrolling through the gantt chart.
*/
private final class FixedZoomNumberAxis extends NumberAxis {
/**
*
*/
private static final long serialVersionUID = 4905313860425556133L;
FixedZoomNumberAxis() {
super(null);
}
@Override
public void resizeRange(final double percent, final double anchorValue) {
if (percent > 0.0) {
super.resizeRange(percent, anchorValue);
} else {
this.setRange(0, GanttChartBuilder.this.lastTurnNumber + 10);
}
}
}
}